
/* GCSx
** SCENEEDITLAYER.H
**
** Scene editing- tile layers
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_SCENEEDITLAYER_H_
#define __GCSx_SCENEEDITLAYER_H_

class SceneEditLayerTools : public Dialog {
private:
    class ToolSelect* toolSelect;
    class SceneEditLayer* paintArea;
    class FrameWindow* myFrame;

    enum {        
        // For tool popup selections
        TOOLSELECT_NUM_TOOLS = 14,
    };
    
    static const ToolSelect::ToolIconStruct toolIcons[TOOLSELECT_NUM_TOOLS];

    // enables/disables contiguous checkbox
    void adjustForTool();
    
    // When we switch between sprite/non-sprite tools, we remember the
    // previous tools so we can switch back and forth
    int otherModeToolL;
    int otherModeToolR;

public:
    enum {
        // Individual buttons
        ID_TOOLL = 1,
        ID_TOOLR,
        ID_COPY,
        ID_PASTE,
        
        // Setting buttons
        ID_CONTIGUOUS,
        ID_GRID,
    };

    int* toolLPtr;
    int* toolRPtr;
    int* contiguousPtr;
    int* gridPtr;

    SceneEditLayerTools(class SceneEditLayer* tPaintArea, int* toolL, int* toolR, int* contiguous, int* grid);
    ~SceneEditLayerTools();
    int event(int hasFocus, const SDL_Event* event);
    void childModified(Window* modified);

    // Creates a FrameWindow and returns, does not add self to desktop (use ->show() to do that)
    class FrameWindow* createWindowed();
    int wantsToBeDeleted() const;
    WindowSort windowSort() const;
};

class SceneEditLayerList : public Dialog {
private:
    class SceneEditLayer* paintArea;
    class FrameWindow* myFrame;

public:
    enum {
        // Layer list itself
        ID_LAYER = 1,
        // Individual buttons
        ID_LAYERADD,
    };

    int selectedLayer;
    class WLayerListBox* layerList;

    SceneEditLayerList(class SceneEditLayer* tPaintArea);
    ~SceneEditLayerList();
    int event(int hasFocus, const SDL_Event* event);
    void childModified(Window* modified);

    // Creates a FrameWindow and returns, does not add self to desktop (use ->show() to do that)
    class FrameWindow* createWindowed();
    int wantsToBeDeleted() const;
    WindowSort windowSort() const;
};

class SceneEditSpriteList : public Dialog {
private:
    class SceneEditLayer* paintArea;
    class FrameWindow* myFrame;
    class WorldEdit* world;
    
    int buildingList;
    void buildSpriteList();

    static int treeViewWrapScript(void* ptr, int code, int command, int check);
    int treeViewScript(int code, int command, int check);

    static int treeViewWrapOther(void* ptr, int code, int command, int check);
    int treeViewOther(int code, int command, int check);

public:
    class TreeView* spriteList;

    SceneEditSpriteList(class SceneEditLayer* tPaintArea, class WorldEdit* tWorld);
    ~SceneEditSpriteList();

    // Creates a FrameWindow and returns, does not add self to desktop (use ->show() to do that)
    class FrameWindow* createWindowed();
    int wantsToBeDeleted() const;
    WindowSort windowSort() const;
};

class SceneEditLayer : public Window {
private:
    class FrameWindow* myFrame;
    class SceneEditLayerTools* tools;
    class ImageSelect* imagebar;
    class ColorSelect* colorbar;
    class SceneEditLayerList* layerlist;
    class SceneEditSpriteList* spritelist;

    int hover;
    int lastMouseX; // Pixel-based- only tracks during hover, not drag
    int lastMouseY;
    
    // The last pixel-based position we've seen for the mouse,
    // in our client-window coordinates (may be negative)
    int pixelMouseX;
    int pixelMouseY;

    int toolL;
    int toolR;
    int contiguous;
    int enableGrid;
    
    class SceneEdit* scene;
    class WorldEdit* world;
    ColorStore colors;
    
    // Are we in sprite mode? (both tools will be sprite tools)
    // Spawn point being placed- temporary, owned by us
    int spriteMode;
    class SpawnEdit* spawnPlace;
    int spawnPropertiesDone;
    // (in pixels- not tiles)
    int spriteAtCursor; // Show placeholder at cursor or at mouse?
    int spriteShown; // Shows a placeholder sprite
    int spriteShownX;
    int spriteShownY;
    
    // Layers 1) viewable 2) affected 3) cursor/editing
    Uint32 layersView;
    Uint32 layersDim; // Of view, which layers are dimmed when not current
    Uint32 layersAffect; // All layers must have same size/tile size
    Uint32 layersAvailable; // All tile layers
    int numLayers; // Counts all layers, not just tile layers
    int cursorLayer;
    int cursorX;
    int cursorY;
    int cursorW;
    int cursorH;
    // (sprite cursor is invisible on it's own)
    int cursorSpriteX;
    int cursorSpriteY;

    // "virtual" cursor for dragging- where the cursor would be if no limits
    int virtualCursorX;
    int virtualCursorY;
    
    // Storage for editing- layers not being affected have NULL pointers
    // [0] is main storage, [1] is extended layer if in use, [2] is effects layer
    // Contains a copy of layer with edits OR a floating selection,
    // if selection mode is not SELECTION_OUTLINE
    Uint32* layerEdit[MAX_LAYERS][3];
    Uint8* selected;       // Current selected area as a bitmask; when selection
    Uint8* backupSelected; // is floating, corresponds to data in layerEdit
    int selectedPitch; // (bytes wide of selected/backupSelected)
    Rect selectionRect;
    Rect backupSelectionRect;
    // Selection mode
    enum {
        SELECTION_OUTLINE = 0, // Draw outline only, no data
        SELECTION_OPAQUE,      // Paste/ovelay layerEdit as-is
    };
    int selectionMode;
    // Move selection; gaurunteed to be 0 if SELECTION_OUTLINE
    int selectionXOffs;
    int selectionYOffs;
    // Selected spawns, by spawn id
    std::set<int> selectedSpawns;
    Rect selectedSpawnRect; // Bounding box for dirties, etc.
    std::set<int> backupSelectedSpawns;
    Rect backupSelectedSpawnRect;
    
    int numTiles; // Cursor layer only
    int tileWidth; // Affected layers only
    int tileHeight; // Affected layers only
    int layerWidth; // Affected layers only
    int layerHeight; // Affected layers only
    int layerExt; // Cursor layer only

    int haveFocus;
    int partialFocus;
    
    int toolStartX; // Normally tiles; pixels for spawn editing
    int toolStartY;
    int toolLastX; // Normally tiles; pixels for spawn editing
    int toolLastY;
    int toolActive;
    Uint32 toolData;
    Uint32 toolExt;
    Uint32 toolFx;
    Uint32 toolDataMask;
    Uint32 toolExtMask;
    Uint32 toolFxMask;
    int toolCtrl;
    int toolAlt;
    int toolContiguous;
    int startToolOnMove;
    
    // Min and max x/y for pen tool, to save undo
    int toolMinX;
    int toolMinY;
    int toolMaxX;
    int toolMaxY;
    
    // (doesn't set any sort of dirty)
    void refreshData(int cursorLayerOnly = 1, int x1 = 0, int y1 = 0, int x2 = MAX_LAYERTILESIZE, int y2 = MAX_LAYERTILESIZE);
    void applyData(int cursorLayerOnly = 1, int x1 = 0, int y1 = 0, int x2 = MAX_LAYERTILESIZE, int y2 = MAX_LAYERTILESIZE);

    // Move cursor, then call
    void startTool(int button);
    void startToolSelectionDrag();
    void modifyTool(); // Call if CTRL/ALT changes
    void dragTool(int firstTime = 0, int lastTime = 0);
    void finishTool();
    void cancelTool();
    // Enables/disables copy tool based on current situation
    void updateCopyTool();
    
    // Dirty range
    Rect dirtyRange;

    // Set partial dirty by tile; specifically allows ranges outside maximums
    void setDirtyBox(int x1, int y1, int x2, int y2, int selectionFeather = 0);
    void setDirtyRect(const Rect& rect, int selectionFeather = 0);
    // Set by pixel
    void setDirtyPixelBox(int x1, int y1, int x2, int y2);

    // Undo wrappers
    void undoStoreLayerBox(int x1, int y1, int x2, int y2, int cursorLayerOnly = 1) throw_Undo;
    void undoStoreLayer(int x, int y, int w, int h, int cursorLayerOnly = 1) throw_Undo;
    // If selection outline, just stores that
    // If selection floating, stores the data as well
    void undoStoreSelect(int x, int y, int w, int h, int forceStoreData = 0) throw_Undo;

    // Selection tools- they clip
    void selectRect(int x1, int y1, int x2, int y2, int set);
    void selectEllipse(int x, int y, int rx, int ry, int set);
    
    // Drawing tools- they clip- assumes data is layerWidth by layerHeight; &'s mask, |'s set
    void layerDrawLine(int x1, int y1, int x2, int y2, Uint32* data, Uint32 mask, Uint32 set) const;
    void layerDrawBox(int x1, int y1, int x2, int y2, Uint32* data, Uint32 mask, Uint32 set) const;
    void layerDrawRect(int x1, int y1, int x2, int y2, Uint32* data, Uint32 mask, Uint32 set) const;
    void layerDrawEllipse(int x, int y, int rx, int ry, Uint32* data, Uint32 mask, Uint32 set) const;
    void layerDrawEllipseFill(int x, int y, int rx, int ry, Uint32* data, Uint32 mask, Uint32 set) const;
    // (matchMask is what bits we pay attention to in data1 that must match to fill)
    Rect layerFloodFill(int x, int y, Uint32 matchMask, Uint32* data1, Uint32 mask1, Uint32 set1, Uint32* data2 = NULL, Uint32 mask2 = 0, Uint32 set2 = 0) const;
    // (non-contiguous flood fill)
    Rect layerMatchFill(int x, int y, Uint32 matchMask, Uint32* data1, Uint32 mask1, Uint32 set1, Uint32* data2 = NULL, Uint32 mask2 = 0, Uint32 set2 = 0) const;

    // If tools should modify the spawn selection (ie, there is a spawn
    // selection and current tool is a spawn tool or there is no regular selection)
    int useSpawnSelection() const;

    // Clear a selection or merge a floation selection; stores undo
    void doneSelection() throw_Undo;
    // Clear selection rectangle, discard any floating selection
    void clearSelection(int storeUndo = 1) throw_Undo;
    // Take an existing outline selection and floats it; replaces
    // old data with empty; Stores undo unless copyPrep is true
    // Doesn't apply change to data if copyPrep is true
    // Just deletes data if delSel is true
    void floatSelection(int copyPrep = 0, int delSel = 0) throw_Undo;
    // Copy an outline or floating selection to clipboard, doesn't
    // affect layer or selection in any way
    void copySelection();
    // Paste clipboard as a new selection; first merges or removes
    // any existing selection; Stores undo; returns TOOLS_SELECT or TOOLS_SELECTIEM
    // if paste successful
    int pasteSelection() throw_Undo;
    // Fill selection with empty or delete a floating selection
    // Stores undo
    void deleteSelection() throw_Undo;
    // Merges a floating selection if one exists
    // Stores undo
    void mergeSelection() throw_Undo;
    // Optionally deselects empties (only if empty on all affected layers)
    void fixSelectionRect(int deselEmpty = 0);
    // Returns true if a given tile x/y is within the current selection area
    int isInSelection(int x, int y) const;

    // (after this, you should call reloadLayerStats())
    void initAvailableLayers();
    // (after this, you must load or reload layer data!)
    void reloadLayerStats(int resizeView = 0);
    void moveCursor(int newX, int newY); // IN PIXELS
    void moveSelection(int newX, int newY); // In tiles
    void mousePointer();
    void mousePointer(int mouseX, int mouseY);
    void updateTitlebar();
    void setUsesExt(int usesExt);

    // Scans in a given direction for a blank or non-blank
    // based on what's under the cursor currently (like in textmode)
    void scanCursor(int xDir, int yDir);

    // Updates sprite-shown variables to match current mouse/cursor/tool settings
    // Sets dirty as necessary
    void updateSpriteShown();
    
    // Spawn editing- each function similar to non-spawn function
    void setDirtySelectedSpawns();
    void copySpawnSelection();
    void deleteSpawnSelection() throw_Undo;
    // (just unselects everything, no modifications)
    void clearSpawnSelection(int storeUndo = 1) throw_Undo;
    // Deselect any selected spawn that isn't on one of our affected layers
    // Then fix spawn selection rectangle to match
    void recheckSpawnSelection();
    void undoStoreSpawnSelect() throw_Undo;
    // (as a bonus, returns spawn ID)
    int isInSpawnSelection(int x, int y);
    void startToolSpawnSelectionDrag();
    void selectSpawnRect(int x1, int y1, int x2, int y2, int select = 1, int selectAll = 0);
    void moveSpawnSelection(int byX, int byY, int skipUndo = 1);

public:
    // Opens the window
    SceneEditLayer(SceneEdit* myScene) throw_File;
    ~SceneEditLayer();

    // Called by tool panel to tell us to refresh view or visible/affected layers
    void refresh();
    void refreshLayers(Uint32 newLayersAffect = 0, Uint32 newLayersView = 0, Uint32 newLayersDim = 0);
    // Called by tool panel to tell us tool was selected via key hold
    void startToolIfMove();
    // Called by tool panel when new tool(s) are selected
    void newToolSelected();
    // Called by sprite panel (takes ownership)
    void setSpawnPlace(class SpawnEdit* newSpawn);
    // Used by undo system
    void swapSelectionParameters(int& xoffs, int& yoffs, int& mode);
    void updatedSpawnSelection();

    CommandSupport supportsCommand(int code) const;
    int event(int hasFocus, const SDL_Event* event);
    void display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset);
    WindowType windowType() const;
    void siblingModified(Window* modified);
    // @TODO: void undoNotify(int undoType, int undoItemId, int undoItemSubId, int uX, int uY, int uW, int uH);
    // (attempt to move cursor to an affected layer and scroll to view an affected area)
};

#endif

